/*-------------------------------------------------------------------------
 *
 * buf_init.c
 *	  缓冲区管理器初始化例程
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/storage/buffer/buf_init.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "storage/buf_internals.h"
#include "storage/bufmgr.h"
#include "storage/proc.h"

BufferDescPadded *BufferDescriptors;
char	   *BufferBlocks;
ConditionVariableMinimallyPadded *BufferIOCVArray;
WritebackContext BackendWritebackContext;
CkptSortItem *CkptBufferIds;


/*
 * 数据结构：
 *		缓冲区存在于自由列表和查找数据结构中。
 *
 *
 * 缓冲区查找：
 *		有两个重要说明。首先，缓冲区必须在 IO 开始之前可用于查找。
 *		否则，第二个尝试读取缓冲区的进程将会
 *		分配自己的副本，缓冲池将
 *		变得不一致。
 *
 * 缓冲区替换：
 *		参见 freelist.c。缓冲区在数据管理器使用或在 IO 过程中
 *		无法被替换。
 *
 *
 * 同步/锁定：
 *
 * IO_IN_PROGRESS -- 这是缓冲区描述符中的一个标志。
 *		当 IO 启动时必须设置，并在 IO 结束时清除。
 *		它的目的是确保一个进程不会在另一个进程
 *		使用缓冲区时开始使用它。参见 WaitIO 和相关的例程。
 *
 * refcount -- 计算持有缓冲区锁定的进程数量。
 *		在 IO 期间和 BufferAlloc() 之后，缓冲区被锁定。
 *		锁定必须在事务结束之前释放。为了效率，
 *		如果某个后端多次锁定缓冲区，则共享 refcount 不会增加。
 *		请检查 bufmgr.c 中的 PrivateRefCount 基础设施。
 */


/*
 * 初始化共享缓冲池
 *
 * 这在共享内存初始化期间调用一次（可以是在
 * postmaster 中，或是在独立后端中）。
 */
void InitBufferPool(void)
{
	bool		fc_foundBufs,
				fc_foundDescs,
				fc_foundIOCV,
				fc_foundBufCkpt;

	/* 将描述符对齐到缓存行边界。 */
	BufferDescriptors = (BufferDescPadded *)
		ShmemInitStruct("Buffer Descriptors",
						NBuffers * sizeof(BufferDescPadded),
						&fc_foundDescs);

	BufferBlocks = (char *)
		ShmemInitStruct("Buffer Blocks",
						NBuffers * (Size) BLCKSZ, &fc_foundBufs);

	/* 将条件变量对齐到缓存行边界。 */
	BufferIOCVArray = (ConditionVariableMinimallyPadded *)
		ShmemInitStruct("Buffer IO Condition Variables",
						NBuffers * sizeof(ConditionVariableMinimallyPadded),
						&fc_foundIOCV);

	/*
	 * 用于排序待检查点缓冲区 ID 的数组位于
	 * 共享内存中，以避免在运行时分配大量
	 * 内存。因为那是在检查点中间，或者
	 * 当检查点程序重新启动时，内存分配失败将会
	 * 带来痛苦。
	 */
	CkptBufferIds = (CkptSortItem *)
		ShmemInitStruct("Checkpoint BufferIds",
						NBuffers * sizeof(CkptSortItem), &fc_foundBufCkpt);

	if (fc_foundDescs || fc_foundBufs || fc_foundIOCV || fc_foundBufCkpt)
	{
		/* 应该找到所有这些，或都不找到 */
		Assert(fc_foundDescs && fc_foundBufs && fc_foundIOCV && fc_foundBufCkpt);
		/* 注意：此路径仅在 EXEC_BACKEND 情况下采取 */
	}
	else
	{
		int			fc_i;

		/*
		 * 初始化所有缓冲区头。
		 */
		for (fc_i = 0; fc_i < NBuffers; fc_i++)
		{
			BufferDesc *fc_buf = GetBufferDescriptor(fc_i);

			CLEAR_BUFFERTAG(fc_buf->tag);

			pg_atomic_init_u32(&fc_buf->state, 0);
			fc_buf->wait_backend_pgprocno = INVALID_PGPROCNO;

			fc_buf->buf_id = fc_i;

			/*
			 * 最初将所有缓冲区链接在一起作为未使用。此列表的后续
			 * 管理由 freelist.c 完成。
			 */
			fc_buf->freeNext = fc_i + 1;

			LWLockInitialize(BufferDescriptorGetContentLock(fc_buf),
							 LWTRANCHE_BUFFER_CONTENT);

			ConditionVariableInit(BufferDescriptorGetIOCV(fc_buf));
		}

		/* 修正链表的最后一个条目 */
		GetBufferDescriptor(NBuffers - 1)->freeNext = FREENEXT_END_OF_LIST;
	}

	/* 初始化其他共享缓冲区管理内容 */
	StrategyInitialize(!fc_foundDescs);

	
/* 初始化每个后端文件刷新的上下文 */
	WritebackContextInit(&BackendWritebackContext,
						 &backend_flush_after);
}

/*
 * BufferShmemSize
 *
 * 计算共享内存的大小用于缓冲池，包括数据页面、缓冲描述符、哈希表等。
 */
Size BufferShmemSize(void)
{
	Size		fc_size = 0;

	/* 缓冲描述符的大小 */
	fc_size = add_size(fc_size, mul_size(NBuffers, sizeof(BufferDescPadded)));
	/* 允许对齐缓冲描述符 */
	fc_size = add_size(fc_size, PG_CACHE_LINE_SIZE);

	/* 数据页面的大小 */
	fc_size = add_size(fc_size, mul_size(NBuffers, BLCKSZ));

	/* freelist.c 控制的内容大小 */
	fc_size = add_size(fc_size, StrategyShmemSize());

	/* I/O 条件变量的大小 */
	fc_size = add_size(fc_size, mul_size(NBuffers,
								   sizeof(ConditionVariableMinimallyPadded)));
	/* 允许对齐上述内容 */
	fc_size = add_size(fc_size, PG_CACHE_LINE_SIZE);

	/* bufmgr.c 中检查点排序数组的大小 */
	fc_size = add_size(fc_size, mul_size(NBuffers, sizeof(CkptSortItem)));

	return fc_size;
}
